package com.zf.bm.common.core.shiro.config;

import com.zf.bm.common.core.shiro.realm.SystemRealm;
import com.zf.bm.common.exception.SystemException;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.AbstractCachingConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Stream;

@Configuration
@EnableConfigurationProperties(ShiroConfig.class)
@AutoConfigureAfter(AbstractCachingConfiguration.class)
public class ShiroAutoConfig {

    @Resource
    private CacheManager cacheManager;
    @Resource
    private ShiroConfig shiroConfig;
    @Bean
    public Realm systemRealm(){
        SystemRealm systemRealm = new SystemRealm();
        // 设置凭证匹配器
//        systemRealm.setCredentialsMatcher(hashedCredentialsMatcher());
//        systemRealm.setCachingEnabled(true);
        return systemRealm;
    }

    @Bean
    public SecurityManager securityManager(Realm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
//        securityManager.setCacheManager(cacheManager());
        securityManager.setSessionManager(sessionManager());
        // 关键：设置 SecurityUtils 的 SecurityManager
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    /**
     * 设置sessionManager相关参数，如超时时间，sessionId名称
     *
     */
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setCacheManager(ehCacheManager());
        sessionManager.setSessionDAO(enterpriseCacheSessionDAO());
        //超时时间，默认15min,会话超时；单位毫秒
        long sessionTimeout = shiroConfig.getSessionTimeout() * (60 * 1000);
        //失效session清理时间
        sessionManager.setSessionValidationInterval(sessionTimeout/2);
        sessionManager.setGlobalSessionTimeout(sessionTimeout);
        return sessionManager;
    }
    /**
     * 密码加密规则
     *
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
       HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(shiroConfig.getPwdHashName());
        credentialsMatcher.setHashIterations(shiroConfig.getPwdIterations());
//        credentialsMatcher.setStoredCredentialsHexEncoded(true); // 改为 true，匹配十六进制编码
        return credentialsMatcher;
    }

//         配置 Shiro 过滤器
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();

        chain.addPathDefinition("/validateCode","anon");
        shiroConfig.getFilteRules().forEach(rule->{
            if (rule.split("-->").length !=2 ){
                throw new SystemException("过滤规则配置不正确，格式：url->filters");
            }
            Stream.of(rule.split("-->")[0].split(","))
                    .forEach(url->chain.addPathDefinition(url,rule.split("--")[1]));
        });
        return chain;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        //创建Shiro的拦截的拦截器 ，用于拦截我们的用户请求
        ShiroFilterFactoryBean shiroFilter=new ShiroFilterFactoryBean();
        //设置Shiro的安全管理，设置管理的同时也会指定某个Realm 用来完成我们权限分配
        shiroFilter.setSecurityManager(securityManager);
        //用于设置一个登录的请求地址，这个地址可以是一个html或jsp的访问路径，也可以是一个控制器的路径
        //作用是用于通知Shiro我们可以使用这里路径转向到登录页面，但Shiro判断到我们当前的用户没有登录时就会自动转换到这个路径
        //要求用户完成成功
        shiroFilter.setLoginUrl("/");
        //登录成功后转向页面，由于用户的登录后期需要交给Shiro完成，因此就需要通知Shiro登录成功之后返回到那个位置
        shiroFilter.setSuccessUrl("/success");
        //定义一个Map集合，这个Map集合中存放的数据全部都是规则，用于设置通知Shiro什么样的请求可以访问什么样的请求不可以访问
        /**
         * Shiro 的内置过滤器可以实现权限的相关拦截
         * 常用过滤器
         * 1.anon:无需认证
         * 2.authc:必须认证才能访问
         * 3.user：如果使用rememberme功能可以访问
         * 4.perms：对应权限才能访问
         * 5.role：对应角色才能访问
         */
        Map<String,String> map=new LinkedHashMap<String,String>();
        //  /login 表示某个请求的名字    anon 表示可以使用游客什么进行登录（这个请求不需要登录）
        map.put("/test/login","anon");
        //map.put("/ai/**","anon");
        //我们可以在这里配置所有的权限规则这列数据真正是需要从数据库中读取出来
        //或者在控制器中添加Shiro的注解
        //  /admin/**  表示一个请求名字的通配， 以admin开头的任意子孙路径下的所有请求
        //  authc 表示这个请求需要进行认证（登录），只有认证（登录）通过才能访问
        // 注意： ** 表示任意子孙路径
        //       *  表示任意的一个路径
        //       ? 表示 任意的一个字符
        map.put("/admin/**","authc");
        map.put("/user/**","authc");
        // 表示这个请求需要有对应权限才能访问
//        map.put("/admin/add","perms[admin:add]");
//        map.put("/admin/update","perms[admin:update]");
        //表示所有的请求路径全部都需要被拦截登录，这个必须必须写在Map集合的最后面,这个选项是可选的
        //如果没有指定/** 那么如果某个请求不符合上面的拦截规则Shiro将方行这个请求
        map.put("/**","authc");
        shiroFilter.setFilterChainDefinitionMap(map);
        //用于指定没有权限的页面，当用户访问某个功能是如果Shiro判断这个用户没有对应的操作权限，那么Shiro就会将请求
        //转向到这个位置，用于提示用户没有操作权限
        shiroFilter.setUnauthorizedUrl("/noPermission");
        return shiroFilter;
    }

//    @Bean
//    public CacheManager cacheManager() {
//        return new MemoryConstrainedCacheManager();
//    }



    /**
     * 设置ehcache缓存方式
     *
     */
    @Bean
    public EhCacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
//        ehCacheManager.setCacheManager(((EhCacheManager)cacheManager).getCacheManager());
        return ehCacheManager;
    }

    /**
     * 设置sessionId名称
     *
     */
    @Bean
    public EnterpriseCacheSessionDAO enterpriseCacheSessionDAO(){
        EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO();
        enterpriseCacheSessionDAO.setSessionIdGenerator(session -> "zfInfo_"+ UUID.randomUUID()
                .toString().replace("-",""));
        enterpriseCacheSessionDAO.setActiveSessionsCacheName(shiroConfig.getActiveSessionCacheName());
        return enterpriseCacheSessionDAO;
    }

    /**
     * 添加生命周期处理器
      */
    @Bean
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启shiro的注解，例如@RequireRoles @RequireUser @RequirePermissions
     * 需要借助SpringAOP来扫描这些注解
     *
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator =  new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
    /**
     *  启用Shiro注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

